import {ViewDf} from "./ViewDf";
import {BindField, BindFunc} from "../BindField";
import {PopWindowPara} from "./PopWindowPara";
export enum HttpType {
    GET,
    POST,
    DELETE,
    PUT,
}

export interface AjaxResp {
    status: number;
    content: string;
}


export class LocalName {
    static input = "input";
    static button = "button";
    static textarea = "textarea";
    static select = "select";
    static a = "a";
    static q = "q";
    static div = "div";
    static tr = "tr";
    static td = "td";
    static th = "th";
    static tbody = "tbody";
    static span = "span";
}


export class InputType {
    static button = "button";
    static checkbox = "checkbox";
    static file = "file";
    static hidden = "hidden";
    static password = "password";
    static radio = "radio";
    static reset = "reset";
    static submit = "submit";
    static text = "text";
}

export interface PopWindow {
    close();
}


export class df {

    static initCalendarID(year: string, month: string, day: string
        , onChange?: (val: Date) => void) {
        df.initCalendar(df.htmlSelect(year), df.htmlSelect(month), df.htmlSelect(day), onChange);
    }

    static setCookie(key: string, val: string|number, expiresTime?: number) {
        if (expiresTime == null)
            expiresTime = 24 * 60 * 60 * 1000;
        let ex = new Date();
        ex.setTime(ex.getTime() + expiresTime)
        document.cookie = `${key}=${val};path=/;expires=` + ex.toUTCString();
    }

    static clearCookie(key: string) {
        let ex = new Date();
        ex.setTime(ex.getTime() - 1)
        document.cookie = `${key}=;path=/;expires=` + ex.toUTCString();
    }

    /**
     * 将对象转为可绑定对象
     * @param className 对象类名或对象
     * @returns {any}
     */
    static bindAble<T>(className: {new(): T}|T): T {
        if (typeof className === "function") {
            let ret = new (className as {new(): T})();
            BindField.init(ret)
            return ret;
        }
        BindField.init(className)
        return className as T;
    }

    /**
     * 创建一个绑定函数
     * @param func 绑定函数
     * @param cancelDoubleBind 是否取消双向绑定
     * @param onSet 赋值事件回调
     * @returns {BindFunc}
     */
    static bind(func: (e: HTMLElement|null) => any, cancelDoubleBind?: boolean, onSet?: (e: HTMLElement|null, val: any, bind: BindField, func: BindFunc) => {val: any, err: Error|null, displayed: boolean}) {
        return new BindFunc(func, !!cancelDoubleBind, onSet);
    }


    /**
     * 设置body内容
     */
    static setBody(ele: string|HTMLElement) {
        df.setEle(document.body, ele);
    }

    /**
     * 正则提取url参数
     * @param reg
     * @returns {any}
     */
    static getUrlPara(reg: RegExp): string |null {
        let res = reg.exec(window.location.href);
        if (res)
            return res[1];
        return null;
    }

    /**
     * 日历
     * @param year
     * @param month
     * @param day
     * @param onChange
     */
    static initCalendar(year: HTMLSelectElement, month: HTMLSelectElement, day: HTMLSelectElement
        , onChange?: (val: Date) => void) {
        if (year == null || month == null || day == null)
            return;

        let today = new Date();


        //初始化年列表
        for (let i = 0; i < 100; i++) {
            let op = document.createElement("option");
            let y = (today.getFullYear() - i) + "";
            op.value = y;
            op.innerHTML = y;
            year.add(op);
        }


        //月
        for (let i = 1; i <= 12; i++) {
            let op = document.createElement("option");
            op.value = i + "";
            op.innerHTML = " " + i;
            if (i == today.getMonth() + 1)
                op.selected = true;

            month.add(op);
        }

        //日(日总数,选中的日)
        let fillDay = (dayCount: number, daySelect: number) => {

            day.innerHTML = "";
            let i = 1;
            let lastOp: HTMLOptionElement|null = null;
            for (; i <= dayCount; i++) {
                let op = document.createElement("option");
                op.value = i + "";
                op.innerHTML = " " + i + "";
                if (i == daySelect)
                    op.selected = true;
                day.add(op);
                lastOp = op;
            }

            if (daySelect >= i && lastOp != null)
                lastOp.selected = true;
        };

        let todayEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
        fillDay(todayEnd.getDate(), today.getDate());

        let changeEv = () => {
            if (onChange)
                onChange(new Date(parseInt(year.value), parseInt(month.value) - 1, parseInt(day.value)));
        };

        month.onchange = ev => {
            let end = new Date(parseInt(year.value), parseInt(month.value), 0);
            fillDay(end.getDate(), parseInt(day.value));

            changeEv();
        };

        year.onchange = ev => {
            if (month.value == "2")
                month.onchange(ev);

            changeEv();
        };

        day.onchange = ev => {
            changeEv();
        };
    }

    /**
     * 提取html中的文本
     * @param html
     * @returns {string}
     */
    static getHtmlText(html: string) {
        var start_ptn = /<\/?[^>]*>/g;      //过滤标签开头
        var end_ptn = /[ | ]*\n/g;            //过滤标签结束
        var space_ptn = /&nbsp;/ig;

        return html.replace(start_ptn, " ").replace(end_ptn, " ").replace(space_ptn, " ");
    }

    /**
     * 设置dom元素的内容
     * @param elem dom元素或id名
     * @param args
     */
    static setEle(elem: HTMLElement|string|number, args: any) {
        df.addEle(elem, args, true);
    }

    static onCatchError = (err: Error) => {
        df.msgErr(err + "", 10 * 1000);
        console.error(err);
    }

    /**
     * 添加dom元素的内容
     * @param elem dom元素或id名
     * @param args
     * @param clear
     */
    static addEle(elem: HTMLElement|string|number, args: any, clear?: boolean) {
        if (typeof elem === "string" || typeof elem === "number") {
            elem = df.html(elem + "");
        }

        if (elem == null) {
            return;
        }

        if (clear)
            (elem as HTMLElement).innerHTML = "";

        if (args == null)
            return;

        if (args instanceof Function) {
            try {
                args();
            } catch (e) {
                df.onCatchError(e);
            }
        }
        // else if (args instanceof Node) {
        // elem.appendChild(args);
        // }
        else if (args instanceof Array) {
            for (var a of args) {
                df.addEle(elem, a);
            }
        }
        else if ((args as Node).localName !== void 0) {
            (elem as HTMLElement).appendChild(args);
        }
        else {
            if ((elem as HTMLElement).children.length > 0) {
                (elem as HTMLElement).appendChild(document.createTextNode(args + ""));
            }
            else {
                (elem as HTMLElement).innerHTML += args + "";
            }
        }
    }

    static htmlButton(id: string|number) {
        return <HTMLButtonElement>document.getElementById(id + "");
    }

    static htmlSelect(id: string|number) {
        return <HTMLSelectElement>document.getElementById(id + "");
    }

    static htmlTable(id: string|number) {
        return document.getElementById(id + "") as HTMLTableElement
    }

    static htmlInput(id: string|number) {
        return <HTMLInputElement>document.getElementById(id + "");
    }


    static htmlDiv(id: string|number) {
        return document.getElementById(id + "") as HTMLDivElement;
    }

    static htmlA(id: string|number) {
        return document.getElementById(id + "") as HTMLAnchorElement;
    }

    static htmlQ(id: string|number) {
        return document.getElementById(id + "") as HTMLQuoteElement;
    }

    static html(id: string|number) {
        return document.getElementById(id + "") as HTMLElement;
    }

    static htmlTextArea(id: string|number) {
        return document.getElementById(id + "") as HTMLTextAreaElement;
    }


    static EachEleValue(ele: HTMLElement, titleNotNull?: boolean) {
        if (titleNotNull === void 0) {
            titleNotNull = true;
        }
        let val = {};
        df.eachElement(ele, e => {
            if (!e.id)
                return;

            switch (e.localName) {

                case LocalName.input:
                case LocalName.textarea:
                case LocalName.select: {
                    let eInput = <HTMLInputElement>e;

                    if (eInput.type == InputType.checkbox) {
                        val[e.id] = eInput.checked
                        return;
                    }

                    if (eInput.type == InputType.button)
                        return;

                    if (eInput.type == InputType.reset)
                        return;

                    if (eInput.title && eInput.value == "") {
                        eInput.select();
                        throw e.title + " 不能为空";
                    }
                    val[e.id] = eInput.value;
                    break;
                }
            }
        });

        return val;
    }

    static appendJS(str: string, ele: HTMLElement) {
        let scriptRegExp = /<script[^>]*>((.|\n|\r)*?(?=<\/script>))<\/script>/ig;
        let result: RegExpExecArray|null = null;
        while ((result = scriptRegExp.exec(str)) != null) {
            var script = document.createElement("script");
            script.text = result[1];
            ele.appendChild(script);
        }
    }

    ///未完成,只能执行代码,src无效
    static execJS(str: string) {
        df.appendJS(str, document.getElementsByTagName("head")[0]);
        //return str.replace(/<script[^>]*src=(?:"|')([^'"]+)(?:"|')[^>]*>([^<>]*)<\/script>/ig, function (all, src) {
        //    var script = document.createElement("script");
        //    script.src = src;
        //    document.getElementsByTagName("head")[0].appendChild(script);
        //    return "";
        //}).replace(/<script[^>]*>([^<>]*)<\/script>/ig, function (all, text) {
        //    var script = document.createElement("script");
        //    script.text = text;
        //    document.getElementsByTagName("head")[0].appendChild(script);
        //    return "";
        //});
    }

    static getContentType(xml: XMLHttpRequest) {
        return xml.getResponseHeader("Content-Type") as string;
    }

    static isJsonResponse(xml: XMLHttpRequest) {
        return df.getContentType(xml).toLowerCase().indexOf("json") >= 0;
    }

    static classRemove(ele: HTMLElement, name: string) {
        let res = ele.className.split(" ");
        let newClass = "";
        for (let c in res) {
            if (res[c] == name)
                continue;
            else
                newClass += res[c] + " ";
        }

        ele.className = newClass;
    }

    /**
     * 关闭进度窗口
     */
    static loadStop() {
        if (!window.document) {
            return
        }
        let spinner = document.getElementById("load_global");
        if (spinner)
            document.body.removeChild(spinner);

        let cover_div = document.getElementById("load_cover");
        if (cover_div)
            document.body.removeChild(cover_div);

    }

    /**
     *
     * @returns {boolean}
     */
    static isLoading(): boolean {
        return document.getElementsByClassName("spinner").length > 0;
    }


    /**
     * 弹出对话框
     * @param cont 内容
     * @param title 标题
     * @param ext
     * @returns {PopWindow}
     */
    static alert(cont: string|HTMLElement, title?: string|HTMLElement, ext: PopWindowPara = {}): PopWindow {
        return df.popWindow(cont, title, ext.onClose, !ext.notCover);
    }

    /**
     * 带确定按钮的弹窗
     * @param cont
     * @param title
     * @param onOk
     * @param ext
     * @returns {PopWindow}
     */
    static alertOk(cont: string|HTMLElement, title?: string|HTMLElement, onOk?: (e: PopWindow) => void, ext: PopWindowPara = {}): PopWindow {

        let wind = df.popWindow(ViewDf.okWindow(cont, e => {

            try {
                if (onOk) {
                    onOk(wind);
                }
            } catch (e) {
                df.onCatchError(e)
            }
        }), title, ext.onClose, !ext.notCover, ext.isErr);

        return wind;
    }

    /**
     * 弹出错误对话框
     * @param cont 内容
     * @param title 标题
     * @param ext
     * @returns {PopWindow}
     */
    static alertErr(cont: string|HTMLElement, title?: string|HTMLElement, ext: PopWindowPara = {}): PopWindow {
        return df.popWindow(cont, title, ext.onClose, !ext.notCover, true);
    }

    /**
     * 弹出一个会自动关闭的消息窗口
     * @param cont 内容
     * @param closeTime 关闭时间(毫秒)
     * @param ext
     * @returns {PopWindow}
     */
    static msg(cont: string|HTMLElement, closeTime?: number, ext: PopWindowPara = {}) {
        if (!window.document) {
            window.alert(cont)
            return;
        }

        let wind = df.popWindow(null, cont, ext.onClose, !ext.notCover, ext.isErr);

        if (closeTime === void 0) {
            closeTime = 3000;
        }

        if (closeTime > 0) {
            setTimeout(() => {
                wind.close()
            }, closeTime);
        }

        return wind;
    }

    static msgErr(cont: string|HTMLElement, closeTime?: number, ext: PopWindowPara = {}) {
        ext.isErr = true;
        return df.msg(cont, closeTime, ext);
    }

    static popID = 0;

    /**
     * 弹出对话框
     * @param content 内容
     * @param title 标题
     * @param onClose 关闭事件
     * @param cover 是否覆盖整个窗口
     * @returns {PopWindow}
     */
    static popWindow(content: string|HTMLElement|null, title?: string|HTMLElement, onClose?: () => void|boolean, cover?: boolean, err?: boolean) {

        if (cover && !df.htmlDiv("load_cover")) {
            var divCover = document.createElement("div");
            divCover.className = "cover_div cover_black"
            document.body.appendChild(divCover);
        }

        let id = ++df.popID;


        let pop = ViewDf.popWindow(id, content, title, err) as HTMLDivElement;
        document.body.appendChild(pop);
        let pop_cont = df.htmlDiv("pop_cont" + id);

        let reSize = () => {
            if (pop.offsetWidth < document.documentElement.clientWidth) {
                pop.style.marginLeft = (Math.floor((document.documentElement.clientWidth - pop.offsetWidth) / 2) | 3) + "px";
            }
            pop_cont.style.maxWidth = document.documentElement.clientWidth - 40 + "px";
            if (pop.offsetHeight < document.documentElement.clientHeight) {
                pop.style.marginTop = (Math.floor((document.documentElement.clientHeight - pop.offsetHeight) / 3) | 3) + "px";
            }
            pop_cont.style.maxHeight = document.documentElement.clientHeight - 45 + "px";
        }
        reSize();


        let resizeTime = setInterval(reSize, 200);


        let popClose = df.htmlQ("pop_close" + id);

        let wind = {} as PopWindow;
        wind.close = () => {
            try {
                clearInterval(resizeTime);
                //窗口已关闭
                if (!df.htmlDiv("pop_cont" + id)) {
                    return;
                }

                if (onClose) {
                    if (onClose() === false) {
                        return;
                    }
                }

                popClose.onclick = () => {
                };
                if (divCover)
                    document.body.removeChild(divCover);

                if (window.history.pushState) {
                    pop.className += " anim_out";
                    setTimeout(() => {
                        //窗口已关闭
                        if (!df.htmlDiv("pop_cont" + id)) {
                            return;
                        }
                        document.body.removeChild(pop);
                    }, 300)
                } else {
                    document.body.removeChild(pop);
                }

            } catch (e) {
                df.onCatchError(e);
            }
        }

        popClose.onclick = () => {
            wind.close();
        }

        return wind;
    }

    /**
     * 显示加载中进度条
     * @param coverAll 是否覆盖整个窗口
     * @param black 黑色背景
     * @param msg 文字提示
     */
    static loadStart(coverAll?: boolean, black?: boolean, msg?: string) {
        if (!window.document) {
            return
        }

        df.loadStop();
        if (!msg) {
            msg = "加载中"
        }

        let ele = document.createElement("div");
        ele.className = "spinner anim_height"
        ele.id = "load_global";
        ele.innerHTML =
            "<div class=\"spinner-container container1\">" +
            "    <div class=\"circle1\"></div>" +
            "    <div class=\"circle2\"></div>" +
            "    <div class=\"circle3\"></div>" +
            "    <div class=\"circle4\"></div>" +
            "  </div>" +
            "  <div class=\"spinner-container container2\">" +
            "    <div class=\"circle1\"></div>" +
            "    <div class=\"circle2\"></div>" +
            "    <div class=\"circle3\"></div>" +
            "    <div class=\"circle4\"></div>" +
            "  </div>" +
            "  <div class=\"spinner-container container3\">" +
            "    <div class=\"circle1\"></div>" +
            "    <div class=\"circle2\"></div>" +
            "    <div class=\"circle3\"></div>" +
            "    <div class=\"circle4\"></div>" +
            `</div><q style='margin-top: 30px'>${msg}</q>`;

        if (coverAll && !df.htmlDiv("load_cover")) {
            let cover = document.createElement("div");
            cover.id = "load_cover"
            cover.className = "cover_div"
            if (black)
                cover.className = "cover_div cover_black"
            document.body.appendChild(cover);
        }

        document.body.appendChild(ele);
        // ele.style.height = "80px";

    }


    static scrollToTop() {
        window.scrollTo(window.scrollX, 0);
    }

    static setFocus(sel: HTMLInputElement, start: number, end: number) {
        if (sel.setSelectionRange) {
            sel.focus();
            sel.setSelectionRange(start, end);
        } else if ((sel as any).createTextRange) {
            var range = (sel as any).createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    }

    static setFocusEnd(sel: HTMLInputElement) {
        let length = sel.value.length;
        df.setFocus(sel, length, length);
    }

    /**
     * 广度优先遍历element的子成员
     * @param elem HTMLElement或元素id
     * @param callback
     */
    static eachElement(eleme: HTMLElement|string|number|null, callback: (res: HTMLElement) => void|boolean) {
        var elem = eleme as HTMLElement;

        if (typeof eleme === "string") {
            elem = document.getElementById(eleme + "")  as HTMLElement;
        }
        else if (typeof eleme === "number") {
            elem = document.getElementById(eleme + "")  as HTMLElement;
        }

        if (eleme == null)
            return;

        let eleList: HTMLElement[] = [];

        for (; ;) {
            for (let i = 0; i < elem.children.length; i++) {

                let e = elem.children[i] as HTMLElement;

                if (callback(e) === false)
                    return;

                if (!e.children)
                    continue;

                if (e.children.length > 0) {
                    eleList.push(e);
                }
            }

            if (eleList.length == 0)
                return;

            elem = eleList.pop() as HTMLElement;
        }
    }


    /**
     * 将object转换为form表单格式字串
     * @param obj
     * @returns {string}
     */
    static objToForm(obj: Object) {
        let ret = "";
        for (let k in obj) {
            ret += k + "=" + encodeURIComponent(obj[k]) + "&";
        }
        if (ret.length > 0)
            return ret.substr(0, ret.length - 1);

        return ret;
    }

    static ajax<T>(url: string, clas?: {new(): T}): AjaxLoad<T> {
        return new AjaxLoad<T>(url, clas);
    }

    static setPrototypeOf(target: any, proto: any) {
        if ((Object as any).setPrototypeOf)
            (Object as any).setPrototypeOf(target, proto);
        else if (target.__proto__) {
            target.__proto__ = proto;
        }
        else {
            /** IE9 fix - copy object methods from the protype to the new object **/
            for (var prop in proto) {
                target[prop] = proto[prop];
            }
        }
    }
}


export class AjaxLoad<T> {
    http = new XMLHttpRequest();

    constructor(public url: string, public clas?: {new(): T}) {
        try {
            this.http.timeout = 15 * 1000;
        } catch (e) {
        }
    }

    setContentTypeJSON(): this {
        this.http.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        return this;
    }

    setContentTypeForm(): this {
        this.http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        return this;
    }


    failedMsg = true;
    loadProgress = true;

    /**
     * 是否显示错误提示与进度条
     * @param enable
     * @returns {AjaxLoad}
     */
    msgProgress(enable: boolean): this {
        this.failedMsg = enable;
        this.loadProgress = enable;
        return this;
    }

    showMsg(enable: boolean): this {
        this.failedMsg = enable;
        return this;
    }

    showProgress(enable: boolean): this {
        this.loadProgress = enable;
        return this;
    }

    /**
     * sendJSON()get或post参数
     */
    paraObj: Object|null = null;
    /**
     * 是否get请求
     */
    notPost: boolean = false;

    /**
     * 获取sendJSON()请求的结果
     * @returns {Promise<T>}
     */
    resp(): Promise<T> {
        if (this.loadProgress)
            df.loadStart();
        return new Promise<T>((reso, reject) => {
            this.procRes(res => {
                if (this.loadProgress) {
                    df.loadStop();
                }
                let resp: any = null;
                let err: Error|null = null;
                if (res.status == 200) {
                    resp = JSON.parse(res.responseText);
                    if (this.clas)
                        df.setPrototypeOf(resp, this.clas.prototype);
                }
                else {
                    if (res.responseText && res.responseText.length > 0)
                        err = Error(res.responseText);
                    else
                        err = Error(res.status + "网络异常!");
                }
                if (res.status == 200) {
                    reso(resp)
                }
                else {
                    if (this.failedMsg) {
                        df.msgErr(err!.message);
                    }
                    reject(resp);
                }
            });
            this.httpSend()
        });
    }

    /**
     * 根据paraObj参数构造http请求
     */
    httpSend() {
        if (this.notPost) {
            if (this.paraObj)
                this.http.open("GET", this.url + "?" + df.objToForm(this.paraObj), true);
            else
                this.http.open("GET", this.url, true);
            this.http.send(null);
        }
        else {
            this.http.open("POST", this.url, true);
            this.setContentTypeJSON();
            this.http.send(JSON.stringify(this.paraObj));
        }
    }


    /**
     * 设置http请求入参
     * @param obj
     * @param notPost
     * @returns {AjaxLoad}
     */
    sendJSON(obj: Object|null, notPost?: boolean): this {
        if (obj) {
            if (!this.paraObj) {
                this.paraObj = {};
            }

            for (let key in obj) {
                this.paraObj[key] = obj[key];
            }
        }

        this.notPost = !!notPost;
        return this;
    }

    get(onRes?: (res: XMLHttpRequest) => void): this {
        this.procRes(onRes);
        this.http.open("GET", this.url, true);
        return this;
    }

    post(val: string, onRes?: (res: XMLHttpRequest) => void): this {
        this.procRes(onRes);
        this.http.open("POST", this.url, true);
        this.http.send(val);
        return this;
    }


    /**
     * 上传进度
     */
    onSendProg = (ev: ProgressEvent) => {
    }

    /**
     * 下载进度
     */
    onRespProg = (ev: ProgressEvent) => {
    }

    procRes(onRes?: (res: XMLHttpRequest) => void) {

        this.http.onreadystatechange = (ev) => {
            if (this.http.readyState == 4) {
                if (onRes) {
                    try {
                        onRes(this.http);
                    } catch (e) {
                        df.onCatchError(e);
                    }
                }
            }
        };
        this.http.onprogress = this.onRespProg;
        this.http.upload.onprogress = this.onSendProg;


    }
}

if (typeof window === "undefined") {
    df.bind = (func, cancelDoubleBind) => {
        return func(null)
    }
}